/*
Copyright (C) 2018-2019 de4dot@gmail.com

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#if MASM
using System;
using System.Diagnostics;
using Iced.Intel.FormatterInternal;

namespace Iced.Intel.MasmFormatterInternal {
	// GENERATOR-BEGIN: InstrOpKind
	// ⚠️This was generated by GENERATOR!🦹‍♂️
	enum InstrOpKind : byte {
		Register,
		NearBranch16,
		NearBranch32,
		NearBranch64,
		FarBranch16,
		FarBranch32,
		Immediate8,
		Immediate8_2nd,
		Immediate16,
		Immediate32,
		Immediate64,
		Immediate8to16,
		Immediate8to32,
		Immediate8to64,
		Immediate32to64,
		MemorySegSI,
		MemorySegESI,
		MemorySegRSI,
		MemorySegDI,
		MemorySegEDI,
		MemorySegRDI,
		MemoryESDI,
		MemoryESEDI,
		MemoryESRDI,
		Memory64,
		Memory,
		ExtraImmediate8_Value3,
		DeclareByte,
		DeclareWord,
		DeclareDword,
		DeclareQword,
	}
	// GENERATOR-END: InstrOpKind

	struct InstrOpInfo {
		internal const int TEST_RegisterBits = IcedConstants.RegisterBits;

		public FormatterString Mnemonic;
		public InstrOpInfoFlags Flags;
		public byte OpCount;
		public InstrOpKind Op0Kind;
		public InstrOpKind Op1Kind;
		public InstrOpKind Op2Kind;
		public InstrOpKind Op3Kind;
		public InstrOpKind Op4Kind;
		public byte Op0Register;
		public byte Op1Register;
		public byte Op2Register;
		public byte Op3Register;
		public byte Op4Register;
		public sbyte Op0Index;
		public sbyte Op1Index;
		public sbyte Op2Index;
		public sbyte Op3Index;
		public sbyte Op4Index;

		public readonly int GetOpRegister(int operand) =>
			operand switch {
				0 => Op0Register,
				1 => Op1Register,
				2 => Op2Register,
				3 => Op3Register,
				4 => Op4Register,
				_ => throw new ArgumentOutOfRangeException(nameof(operand)),
			};

		public readonly InstrOpKind GetOpKind(int operand) {
			switch (operand) {
			case 0: return Op0Kind;
			case 1: return Op1Kind;
			case 2: return Op2Kind;
			case 3: return Op3Kind;
			case 4: return Op4Kind;
			default:
				Debug.Assert(Op0Kind == InstrOpKind.DeclareByte || Op0Kind == InstrOpKind.DeclareWord || Op0Kind == InstrOpKind.DeclareDword || Op0Kind == InstrOpKind.DeclareQword);
				return Op0Kind;
			}
		}

		public readonly int GetInstructionIndex(int operand) {
			int instructionOperand;
			switch (operand) {
			case 0: instructionOperand = Op0Index; break;
			case 1: instructionOperand = Op1Index; break;
			case 2: instructionOperand = Op2Index; break;
			case 3: instructionOperand = Op3Index; break;
			case 4: instructionOperand = Op4Index; break;
			default:
				Debug.Assert(Op0Kind == InstrOpKind.DeclareByte || Op0Kind == InstrOpKind.DeclareWord || Op0Kind == InstrOpKind.DeclareDword || Op0Kind == InstrOpKind.DeclareQword);
				instructionOperand = -1;
				break;
			}
			return instructionOperand < 0 ? -1 : instructionOperand;
		}

#if INSTR_INFO
		public readonly bool TryGetOpAccess(int operand, out OpAccess access) {
			int instructionOperand;
			switch (operand) {
			case 0: instructionOperand = Op0Index; break;
			case 1: instructionOperand = Op1Index; break;
			case 2: instructionOperand = Op2Index; break;
			case 3: instructionOperand = Op3Index; break;
			case 4: instructionOperand = Op4Index; break;
			default:
				Debug.Assert(Op0Kind == InstrOpKind.DeclareByte || Op0Kind == InstrOpKind.DeclareWord || Op0Kind == InstrOpKind.DeclareDword || Op0Kind == InstrOpKind.DeclareQword);
				instructionOperand = Op0Index;
				break;
			}
			if (instructionOperand < InstrInfo.OpAccess_INVALID) {
				access = (OpAccess)(-instructionOperand - 2);
				return true;
			}
			access = OpAccess.None;
			return false;
		}
#endif

		public readonly int GetOperandIndex(int instructionOperand) {
			int index;
			if (instructionOperand == Op0Index)
				index = 0;
			else if (instructionOperand == Op1Index)
				index = 1;
			else if (instructionOperand == Op2Index)
				index = 2;
			else if (instructionOperand == Op3Index)
				index = 3;
			else if (instructionOperand == Op4Index)
				index = 4;
			else
				index = -1;
			return index < OpCount ? index : -1;
		}

		public InstrOpInfo(FormatterString mnemonic, in Instruction instruction, InstrOpInfoFlags flags) {
			Static.Assert(IcedConstants.MaxOpCount == 5 ? 0 : -1);
			Mnemonic = mnemonic;
			Flags = flags;
			Op0Kind = (InstrOpKind)instruction.Op0Kind;
			Op1Kind = (InstrOpKind)instruction.Op1Kind;
			Op2Kind = (InstrOpKind)instruction.Op2Kind;
			Op3Kind = (InstrOpKind)instruction.Op3Kind;
			Op4Kind = (InstrOpKind)instruction.Op4Kind;
			Static.Assert(TEST_RegisterBits == 8 ? 0 : -1);
			Op0Register = (byte)instruction.Op0Register;
			Static.Assert(TEST_RegisterBits == 8 ? 0 : -1);
			Op1Register = (byte)instruction.Op1Register;
			Static.Assert(TEST_RegisterBits == 8 ? 0 : -1);
			Op2Register = (byte)instruction.Op2Register;
			Static.Assert(TEST_RegisterBits == 8 ? 0 : -1);
			Op3Register = (byte)instruction.Op3Register;
			Static.Assert(TEST_RegisterBits == 8 ? 0 : -1);
			Op4Register = (byte)instruction.Op4Register;
			int opCount = instruction.OpCount;
			OpCount = (byte)opCount;
			switch (opCount) {
			case 0:
				Op0Index = InstrInfo.OpAccess_INVALID;
				Op1Index = InstrInfo.OpAccess_INVALID;
				Op2Index = InstrInfo.OpAccess_INVALID;
				Op3Index = InstrInfo.OpAccess_INVALID;
				Op4Index = InstrInfo.OpAccess_INVALID;
				break;

			case 1:
				Op0Index = 0;
				Op1Index = InstrInfo.OpAccess_INVALID;
				Op2Index = InstrInfo.OpAccess_INVALID;
				Op3Index = InstrInfo.OpAccess_INVALID;
				Op4Index = InstrInfo.OpAccess_INVALID;
				break;

			case 2:
				Op0Index = 0;
				Op1Index = 1;
				Op2Index = InstrInfo.OpAccess_INVALID;
				Op3Index = InstrInfo.OpAccess_INVALID;
				Op4Index = InstrInfo.OpAccess_INVALID;
				break;

			case 3:
				Op0Index = 0;
				Op1Index = 1;
				Op2Index = 2;
				Op3Index = InstrInfo.OpAccess_INVALID;
				Op4Index = InstrInfo.OpAccess_INVALID;
				break;

			case 4:
				Op0Index = 0;
				Op1Index = 1;
				Op2Index = 2;
				Op3Index = 3;
				Op4Index = InstrInfo.OpAccess_INVALID;
				break;

			case 5:
				Op0Index = 0;
				Op1Index = 1;
				Op2Index = 2;
				Op3Index = 3;
				Op4Index = 4;
				break;

			default:
				throw new InvalidOperationException();
			}
		}
	}

	abstract class InstrInfo {
		public const int OpAccess_INVALID = -1;
#if INSTR_INFO
		public const int OpAccess_None = -(int)(OpAccess.None + 2);
		public const int OpAccess_Read = -(int)(OpAccess.Read + 2);
		public const int OpAccess_CondRead = -(int)(OpAccess.CondRead + 2);
		public const int OpAccess_Write = -(int)(OpAccess.Write + 2);
		public const int OpAccess_CondWrite = -(int)(OpAccess.CondWrite + 2);
		public const int OpAccess_ReadWrite = -(int)(OpAccess.ReadWrite + 2);
		public const int OpAccess_ReadCondWrite = -(int)(OpAccess.ReadCondWrite + 2);
		public const int OpAccess_NoMemAccess = -(int)(OpAccess.NoMemAccess + 2);
#else
		public const int OpAccess_None = OpAccess_INVALID;
		public const int OpAccess_Read = OpAccess_INVALID;
		public const int OpAccess_CondRead = OpAccess_INVALID;
		public const int OpAccess_Write = OpAccess_INVALID;
		public const int OpAccess_CondWrite = OpAccess_INVALID;
		public const int OpAccess_ReadWrite = OpAccess_INVALID;
		public const int OpAccess_ReadCondWrite = OpAccess_INVALID;
		public const int OpAccess_NoMemAccess = OpAccess_INVALID;
#endif

		public abstract void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info);

		protected static int GetBitness(CodeSize codeSize) =>
			codeSize switch {
				CodeSize.Code16 => 16,
				CodeSize.Code32 => 32,
				CodeSize.Code64 => 64,
				_ => 0,
			};
	}

	sealed class SimpleInstrInfo : InstrInfo {
		readonly FormatterString mnemonic;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo(string mnemonic) : this(mnemonic, InstrOpInfoFlags.None) { }

		public SimpleInstrInfo(string mnemonic, InstrOpInfoFlags flags) {
			this.mnemonic = new FormatterString(mnemonic);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) =>
			info = new InstrOpInfo(mnemonic, instruction, flags);
	}

	sealed class SimpleInstrInfo_cc : InstrInfo {
		readonly int ccIndex;
		readonly FormatterString[] mnemonics;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_cc(int ccIndex, string[] mnemonics) : this(ccIndex, mnemonics, InstrOpInfoFlags.None) { }

		public SimpleInstrInfo_cc(int ccIndex, string[] mnemonics, InstrOpInfoFlags flags) {
			this.ccIndex = ccIndex;
			this.mnemonics = FormatterString.Create(mnemonics);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var mnemonic = MnemonicCC.GetMnemonicCC(options, ccIndex, mnemonics);
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_mmxmem : InstrInfo {
		readonly FormatterString mnemonic;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_mmxmem(string mnemonic)
			: this(mnemonic, InstrOpInfoFlags.None) {
		}

		public SimpleInstrInfo_mmxmem(string mnemonic, InstrOpInfoFlags flags) {
			this.mnemonic = new FormatterString(mnemonic);
			this.flags = flags | InstrOpInfoFlags.MemSize_Mmx;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) =>
			info = new InstrOpInfo(mnemonic, instruction, flags);
	}

	sealed class SimpleInstrInfo_memsize : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_memsize(int bitness, string mnemonic) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			int instrBitness = GetBitness(instruction.CodeSize);
			var flags = instrBitness == 0 || (instrBitness & bitness) != 0 ? InstrOpInfoFlags.MemSize_Nothing : InstrOpInfoFlags.MemSize_Normal | InstrOpInfoFlags.ShowNoMemSize_ForceSize | InstrOpInfoFlags.ShowMinMemSize_ForceSize;
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_AamAad : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_AamAad(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			if (instruction.Immediate8 == 10) {
				info = default;
				info.Mnemonic = mnemonic;
			}
			else
				info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
		}
	}

	sealed class SimpleInstrInfo_Ib : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_Ib(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = default;
			info.Mnemonic = mnemonic;
			info.OpCount = 1;
			info.Op0Kind = InstrOpKind.ExtraImmediate8_Value3;
			info.Op0Index = OpAccess_Read;
		}
	}

	sealed class SimpleInstrInfo_YD : InstrInfo {
		readonly FormatterString mnemonic_args;
		readonly FormatterString mnemonic_no_args;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_YD(string mnemonic_args, string mnemonic_no_args, InstrOpInfoFlags flags) {
			this.mnemonic_args = new FormatterString(mnemonic_args);
			this.mnemonic_no_args = new FormatterString(mnemonic_no_args);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var shortFormOpKind = instruction.CodeSize switch {
				CodeSize.Unknown => instruction.Op0Kind,
				CodeSize.Code16 => OpKind.MemoryESDI,
				CodeSize.Code32 => OpKind.MemoryESEDI,
				CodeSize.Code64 => OpKind.MemoryESRDI,
				_ => throw new InvalidOperationException(),
			};
			bool shortForm = instruction.Op0Kind == shortFormOpKind;
			if (!shortForm)
				info = new InstrOpInfo(mnemonic_args, instruction, flags);
			else {
				info = default;
				info.Flags = flags;
				info.Mnemonic = mnemonic_no_args;
			}
		}
	}

	sealed class SimpleInstrInfo_DX : InstrInfo {
		readonly FormatterString mnemonic_args;
		readonly FormatterString mnemonic_no_args;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_DX(string mnemonic_args, string mnemonic_no_args, InstrOpInfoFlags flags) {
			this.mnemonic_args = new FormatterString(mnemonic_args);
			this.mnemonic_no_args = new FormatterString(mnemonic_no_args);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var shortFormOpKind = instruction.CodeSize switch {
				CodeSize.Unknown => instruction.Op1Kind,
				CodeSize.Code16 => OpKind.MemorySegSI,
				CodeSize.Code32 => OpKind.MemorySegESI,
				CodeSize.Code64 => OpKind.MemorySegRSI,
				_ => throw new InvalidOperationException(),
			};
			bool shortForm = instruction.Op1Kind == shortFormOpKind &&
				(instruction.SegmentPrefix == Register.None || !FormatterUtils.ShowSegmentPrefix(Register.DS, instruction, options));
			if (!shortForm)
				info = new InstrOpInfo(mnemonic_args, instruction, flags);
			else {
				info = default;
				info.Flags = flags;
				info.Mnemonic = mnemonic_no_args;
			}
		}
	}

	sealed class SimpleInstrInfo_YX : InstrInfo {
		readonly FormatterString mnemonic_args;
		readonly FormatterString mnemonic_no_args;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_YX(string mnemonic_args, string mnemonic_no_args, InstrOpInfoFlags flags) {
			this.mnemonic_args = new FormatterString(mnemonic_args);
			this.mnemonic_no_args = new FormatterString(mnemonic_no_args);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var shortFormOpKind = instruction.CodeSize switch {
				CodeSize.Unknown => instruction.Op0Kind,
				CodeSize.Code16 => OpKind.MemoryESDI,
				CodeSize.Code32 => OpKind.MemoryESEDI,
				CodeSize.Code64 => OpKind.MemoryESRDI,
				_ => throw new InvalidOperationException(),
			};
			bool shortForm = instruction.Op0Kind == shortFormOpKind &&
				(instruction.SegmentPrefix == Register.None || !FormatterUtils.ShowSegmentPrefix(Register.DS, instruction, options));
			if (!shortForm)
				info = new InstrOpInfo(mnemonic_args, instruction, flags);
			else {
				info = default;
				info.Flags = flags;
				info.Mnemonic = mnemonic_no_args;
			}
		}
	}

	sealed class SimpleInstrInfo_XY : InstrInfo {
		readonly FormatterString mnemonic_args;
		readonly FormatterString mnemonic_no_args;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_XY(string mnemonic_args, string mnemonic_no_args, InstrOpInfoFlags flags) {
			this.mnemonic_args = new FormatterString(mnemonic_args);
			this.mnemonic_no_args = new FormatterString(mnemonic_no_args);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var shortFormOpKind = instruction.CodeSize switch {
				CodeSize.Unknown => instruction.Op1Kind,
				CodeSize.Code16 => OpKind.MemoryESDI,
				CodeSize.Code32 => OpKind.MemoryESEDI,
				CodeSize.Code64 => OpKind.MemoryESRDI,
				_ => throw new InvalidOperationException(),
			};
			bool shortForm = instruction.Op1Kind == shortFormOpKind &&
				(instruction.SegmentPrefix == Register.None || !FormatterUtils.ShowSegmentPrefix(Register.DS, instruction, options));
			if (!shortForm)
				info = new InstrOpInfo(mnemonic_args, instruction, flags);
			else {
				info = default;
				info.Flags = flags;
				info.Mnemonic = mnemonic_no_args;
			}
		}
	}

	sealed class SimpleInstrInfo_YA : InstrInfo {
		readonly FormatterString mnemonic_args;
		readonly FormatterString mnemonic_no_args;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_YA(string mnemonic_args, string mnemonic_no_args, InstrOpInfoFlags flags) {
			this.mnemonic_args = new FormatterString(mnemonic_args);
			this.mnemonic_no_args = new FormatterString(mnemonic_no_args);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var shortFormOpKind = instruction.CodeSize switch {
				CodeSize.Unknown => instruction.Op0Kind,
				CodeSize.Code16 => OpKind.MemoryESDI,
				CodeSize.Code32 => OpKind.MemoryESEDI,
				CodeSize.Code64 => OpKind.MemoryESRDI,
				_ => throw new InvalidOperationException(),
			};
			bool shortForm = instruction.Op0Kind == shortFormOpKind;
			if (!shortForm) {
				info = default;
				info.Flags = flags;
				info.Mnemonic = mnemonic_args;
				info.OpCount = 1;
				info.Op0Kind = (InstrOpKind)instruction.Op0Kind;
			}
			else {
				info = default;
				info.Flags = flags;
				info.Mnemonic = mnemonic_no_args;
			}
		}
	}

	sealed class SimpleInstrInfo_AX : InstrInfo {
		readonly FormatterString mnemonic_args;
		readonly FormatterString mnemonic_no_args;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_AX(string mnemonic_args, string mnemonic_no_args, InstrOpInfoFlags flags) {
			this.mnemonic_args = new FormatterString(mnemonic_args);
			this.mnemonic_no_args = new FormatterString(mnemonic_no_args);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var shortFormOpKind = instruction.CodeSize switch {
				CodeSize.Unknown => instruction.Op1Kind,
				CodeSize.Code16 => OpKind.MemorySegSI,
				CodeSize.Code32 => OpKind.MemorySegESI,
				CodeSize.Code64 => OpKind.MemorySegRSI,
				_ => throw new InvalidOperationException(),
			};
			bool shortForm = instruction.Op1Kind == shortFormOpKind &&
				(instruction.SegmentPrefix == Register.None || !FormatterUtils.ShowSegmentPrefix(Register.DS, instruction, options));
			if (!shortForm) {
				info = default;
				info.Flags = flags;
				info.Mnemonic = mnemonic_args;
				info.OpCount = 1;
				info.Op0Kind = (InstrOpKind)instruction.Op1Kind;
				info.Op0Index = 1;
			}
			else {
				info = default;
				info.Flags = flags;
				info.Mnemonic = mnemonic_no_args;
			}
		}
	}

	sealed class SimpleInstrInfo_AY : InstrInfo {
		readonly FormatterString mnemonic_args;
		readonly FormatterString mnemonic_no_args;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_AY(string mnemonic_args, string mnemonic_no_args, InstrOpInfoFlags flags) {
			this.mnemonic_args = new FormatterString(mnemonic_args);
			this.mnemonic_no_args = new FormatterString(mnemonic_no_args);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var shortFormOpKind = instruction.CodeSize switch {
				CodeSize.Unknown => instruction.Op1Kind,
				CodeSize.Code16 => OpKind.MemoryESDI,
				CodeSize.Code32 => OpKind.MemoryESEDI,
				CodeSize.Code64 => OpKind.MemoryESRDI,
				_ => throw new InvalidOperationException(),
			};
			bool shortForm = instruction.Op1Kind == shortFormOpKind &&
				(instruction.SegmentPrefix == Register.None || !FormatterUtils.ShowSegmentPrefix(Register.DS, instruction, options));
			if (!shortForm) {
				info = default;
				info.Flags = flags;
				info.Mnemonic = mnemonic_args;
				info.OpCount = 1;
				info.Op0Kind = (InstrOpKind)instruction.Op1Kind;
				info.Op0Index = 1;
			}
			else {
				info = default;
				info.Flags = flags;
				info.Mnemonic = mnemonic_no_args;
			}
		}
	}

	sealed class SimpleInstrInfo_XLAT : InstrInfo {
		readonly FormatterString mnemonic_args;
		readonly FormatterString mnemonic_no_args;

		public SimpleInstrInfo_XLAT(string mnemonic_args, string mnemonic_no_args) {
			this.mnemonic_args = new FormatterString(mnemonic_args);
			this.mnemonic_no_args = new FormatterString(mnemonic_no_args);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var baseReg = instruction.CodeSize switch {
				CodeSize.Unknown => instruction.MemoryBase,
				CodeSize.Code16 => Register.BX,
				CodeSize.Code32 => Register.EBX,
				CodeSize.Code64 => Register.RBX,
				_ => throw new InvalidOperationException(),
			};
			bool shortForm = instruction.MemoryBase == baseReg &&
				(instruction.SegmentPrefix == Register.None || !FormatterUtils.ShowSegmentPrefix(Register.DS, instruction, options));
			if (!shortForm)
				info = new InstrOpInfo(mnemonic_args, instruction, InstrOpInfoFlags.ShowNoMemSize_ForceSize | InstrOpInfoFlags.IgnoreIndexReg);
			else {
				info = default;
				info.Mnemonic = mnemonic_no_args;
			}
		}
	}

	sealed class SimpleInstrInfo_nop : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;
		readonly Register register;

		public SimpleInstrInfo_nop(int bitness, string mnemonic, Register register) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
			this.register = register;
		}

		static readonly FormatterString str_xchg = new FormatterString("xchg");

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			int instrBitness = GetBitness(instruction.CodeSize);
			if (instrBitness == 0 || (instrBitness & bitness) != 0)
				info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
			else {
				info = default;
				info.Mnemonic = str_xchg;
				info.OpCount = 2;
				Static.Assert(InstrOpKind.Register == 0 ? 0 : -1);
				//info.Op0Kind = InstrOpKind.Register;
				//info.Op1Kind = InstrOpKind.Register;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)register;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op1Register = (byte)register;
				info.Op0Index = OpAccess_None;
				info.Op1Index = OpAccess_None;
			}
		}
	}

	sealed class SimpleInstrInfo_STIG1 : InstrInfo {
		readonly FormatterString mnemonic;
		readonly bool pseudoOp;

		public SimpleInstrInfo_STIG1(string mnemonic) : this(mnemonic, false) { }

		public SimpleInstrInfo_STIG1(string mnemonic, bool pseudoOp) {
			this.mnemonic = new FormatterString(mnemonic);
			this.pseudoOp = pseudoOp;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = default;
			info.Mnemonic = mnemonic;
			Debug.Assert(instruction.OpCount == 2);
			Debug.Assert(instruction.Op0Kind == OpKind.Register && instruction.Op0Register == Register.ST0);
			if (!pseudoOp || !(options.UsePseudoOps && instruction.Op1Register == Register.ST1)) {
				info.OpCount = 1;
				Static.Assert(InstrOpKind.Register == 0 ? 0 : -1);
				//info.Op0Kind = InstrOpKind.Register;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)instruction.Op1Register;
				info.Op0Index = 1;
			}
		}
	}

	sealed class SimpleInstrInfo_STi_ST2 : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_STi_ST2(string mnemonic) {
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			const InstrOpInfoFlags flags = 0;
			if (options.UsePseudoOps && (instruction.Op0Register == Register.ST1 || instruction.Op1Register == Register.ST1)) {
				info = default;
				info.Mnemonic = mnemonic;
			}
			else {
				info = new InstrOpInfo(mnemonic, instruction, flags);
				Debug.Assert(info.Op1Register == (int)Register.ST0);
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op1Register = (byte)Registers.Register_ST;
			}
		}
	}

	sealed class SimpleInstrInfo_ST_STi : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_ST_STi(string mnemonic) {
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
			Debug.Assert(info.Op0Register == (int)Register.ST0);
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op0Register = (byte)Registers.Register_ST;
		}
	}

	sealed class SimpleInstrInfo_STi_ST : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_STi_ST(string mnemonic) {
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
			Debug.Assert(info.Op1Register == (int)Register.ST0);
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op1Register = (byte)Registers.Register_ST;
		}
	}

	sealed class SimpleInstrInfo_monitor : InstrInfo {
		readonly FormatterString mnemonic;
		readonly Register register1;
		readonly Register register2;
		readonly Register register3;

		public SimpleInstrInfo_monitor(string mnemonic, Register register1, Register register2, Register register3) {
			this.mnemonic = new FormatterString(mnemonic);
			this.register1 = register1;
			this.register2 = register2;
			this.register3 = register3;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = default;
			info.Mnemonic = mnemonic;
			info.OpCount = 3;
			info.Op0Kind = InstrOpKind.Register;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op0Register = (byte)register1;
			info.Op1Kind = InstrOpKind.Register;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op1Register = (byte)register2;
			info.Op2Kind = InstrOpKind.Register;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op2Register = (byte)register3;
			info.Op0Index = OpAccess_Read;
			info.Op1Index = OpAccess_Read;
			info.Op2Index = OpAccess_Read;
			if ((instruction.CodeSize == CodeSize.Code64 || instruction.CodeSize == CodeSize.Unknown) && (Register.EAX <= register2 && register2 <= Register.R15D)) {
				info.Op1Register += 0x10;
				info.Op2Register += 0x10;
			}
		}
	}

	sealed class SimpleInstrInfo_mwait : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_mwait(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = default;
			info.Mnemonic = mnemonic;
			info.OpCount = 2;
			info.Op0Kind = InstrOpKind.Register;
			info.Op1Kind = InstrOpKind.Register;
			info.Op0Index = OpAccess_Read;
			info.Op1Index = OpAccess_Read;

			switch (instruction.CodeSize) {
			case CodeSize.Code16:
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)Register.AX;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op1Register = (byte)Register.ECX;
				break;
			case CodeSize.Code32:
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)Register.EAX;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op1Register = (byte)Register.ECX;
				break;
			case CodeSize.Unknown:
			case CodeSize.Code64:
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)Register.RAX;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op1Register = (byte)Register.RCX;
				break;
			}
		}
	}

	sealed class SimpleInstrInfo_mwaitx : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_mwaitx(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = default;
			info.Mnemonic = mnemonic;
			info.OpCount = 3;
			info.Op0Kind = InstrOpKind.Register;
			info.Op1Kind = InstrOpKind.Register;
			info.Op2Kind = InstrOpKind.Register;
			info.Op0Index = OpAccess_Read;
			info.Op1Index = OpAccess_Read;
			info.Op2Index = OpAccess_CondRead;

			switch (instruction.CodeSize) {
			case CodeSize.Code16:
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)Register.AX;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op1Register = (byte)Register.ECX;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op2Register = (byte)Register.EBX;
				break;
			case CodeSize.Code32:
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)Register.EAX;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op1Register = (byte)Register.ECX;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op2Register = (byte)Register.EBX;
				break;
			case CodeSize.Unknown:
			case CodeSize.Code64:
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)Register.RAX;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op1Register = (byte)Register.RCX;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op2Register = (byte)Register.RBX;
				break;
			}
		}
	}

	sealed class SimpleInstrInfo_maskmovq : InstrInfo {
		readonly FormatterString mnemonic;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_maskmovq(string mnemonic, InstrOpInfoFlags flags) {
			this.mnemonic = new FormatterString(mnemonic);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			Debug.Assert(instruction.OpCount == 3);
			var shortFormOpKind = instruction.CodeSize switch {
				CodeSize.Unknown => instruction.Op0Kind,
				CodeSize.Code16 => OpKind.MemorySegDI,
				CodeSize.Code32 => OpKind.MemorySegEDI,
				CodeSize.Code64 => OpKind.MemorySegRDI,
				_ => throw new InvalidOperationException(),
			};
			bool shortForm = instruction.Op0Kind == shortFormOpKind &&
				(instruction.SegmentPrefix == Register.None || !FormatterUtils.ShowSegmentPrefix(Register.DS, instruction, options));
			if (!shortForm)
				info = new InstrOpInfo(mnemonic, instruction, flags);
			else {
				info = default;
				info.Flags = flags;
				info.Mnemonic = mnemonic;
				info.OpCount = 2;
				info.Op0Kind = (InstrOpKind)instruction.Op1Kind;
				info.Op0Index = 1;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)instruction.Op1Register;
				info.Op1Kind = (InstrOpKind)instruction.Op2Kind;
				info.Op1Index = 2;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op1Register = (byte)instruction.Op2Register;
			}
		}
	}

	sealed class SimpleInstrInfo_pblendvb : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_pblendvb(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = default;
			Debug.Assert(instruction.OpCount == 2);
			info.Mnemonic = mnemonic;
			info.OpCount = 3;
			info.Op0Kind = (InstrOpKind)instruction.Op0Kind;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op0Register = (byte)instruction.Op0Register;
			info.Op1Kind = (InstrOpKind)instruction.Op1Kind;
			info.Op1Index = 1;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op1Register = (byte)instruction.Op1Register;
			info.Op2Kind = InstrOpKind.Register;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op2Register = (byte)Register.XMM0;
			info.Op2Index = OpAccess_Read;
		}
	}

	sealed class SimpleInstrInfo_reverse2 : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_reverse2(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = default;
			info.Mnemonic = mnemonic;
			Debug.Assert(instruction.OpCount == 2);
			info.OpCount = 2;
			info.Op0Kind = (InstrOpKind)instruction.Op1Kind;
			info.Op0Index = 1;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op0Register = (byte)instruction.Op1Register;
			info.Op1Kind = (InstrOpKind)instruction.Op0Kind;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op1Register = (byte)instruction.Op0Register;
		}
	}

	sealed class SimpleInstrInfo_OpSize : InstrInfo {
		readonly CodeSize codeSize;
		readonly FormatterString[] mnemonics;

		public SimpleInstrInfo_OpSize(CodeSize codeSize, string mnemonic, string mnemonic16, string mnemonic32, string mnemonic64) {
			this.codeSize = codeSize;
			mnemonics = new FormatterString[4];
			mnemonics[(int)CodeSize.Unknown] = new FormatterString(mnemonic);
			mnemonics[(int)CodeSize.Code16] = new FormatterString(mnemonic16);
			mnemonics[(int)CodeSize.Code32] = new FormatterString(mnemonic32);
			mnemonics[(int)CodeSize.Code64] = new FormatterString(mnemonic64);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			FormatterString mnemonic;
			if (instruction.CodeSize == codeSize)
				mnemonic = mnemonics[(int)CodeSize.Unknown];
			else
				mnemonic = mnemonics[(int)codeSize];
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
		}
	}

	sealed class SimpleInstrInfo_OpSize_cc : InstrInfo {
		readonly CodeSize codeSize;
		readonly int ccIndex;
		readonly FormatterString[] mnemonics;
		readonly FormatterString[] mnemonics_other;

		public SimpleInstrInfo_OpSize_cc(CodeSize codeSize, int ccIndex, string[] mnemonics, string[] mnemonics_other) {
			this.codeSize = codeSize;
			this.ccIndex = ccIndex;
			this.mnemonics = FormatterString.Create(mnemonics);
			this.mnemonics_other = FormatterString.Create(mnemonics_other);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			FormatterString[] mnemonics;
			if (instruction.CodeSize == codeSize)
				mnemonics = this.mnemonics;
			else
				mnemonics = this.mnemonics_other;
			var mnemonic = MnemonicCC.GetMnemonicCC(options, ccIndex, mnemonics);
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
		}
	}

	sealed class SimpleInstrInfo_OpSize2 : InstrInfo {
		readonly FormatterString[] mnemonics;

		public SimpleInstrInfo_OpSize2(string mnemonic, string mnemonic16, string mnemonic32, string mnemonic64) {
			mnemonics = new FormatterString[4];
			mnemonics[(int)CodeSize.Unknown] = new FormatterString(mnemonic);
			mnemonics[(int)CodeSize.Code16] = new FormatterString(mnemonic16);
			mnemonics[(int)CodeSize.Code32] = new FormatterString(mnemonic32);
			mnemonics[(int)CodeSize.Code64] = new FormatterString(mnemonic64);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var mnemonic = mnemonics[(int)instruction.CodeSize];
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
		}
	}

	sealed class SimpleInstrInfo_OpSize2_bnd : InstrInfo {
		readonly FormatterString[] mnemonics;

		public SimpleInstrInfo_OpSize2_bnd(string mnemonic, string mnemonic16, string mnemonic32, string mnemonic64) {
			mnemonics = new FormatterString[4];
			mnemonics[(int)CodeSize.Unknown] = new FormatterString(mnemonic);
			mnemonics[(int)CodeSize.Code16] = new FormatterString(mnemonic16);
			mnemonics[(int)CodeSize.Code32] = new FormatterString(mnemonic32);
			mnemonics[(int)CodeSize.Code64] = new FormatterString(mnemonic64);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			if (instruction.HasRepnePrefix)
				flags |= InstrOpInfoFlags.BndPrefix;
			var mnemonic = mnemonics[(int)instruction.CodeSize];
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_pushm : InstrInfo {
		readonly CodeSize codeSize;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_pushm(CodeSize codeSize, string mnemonic) {
			this.codeSize = codeSize;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			if (instruction.CodeSize != codeSize && instruction.CodeSize != CodeSize.Unknown)
				flags |= InstrOpInfoFlags.ShowNoMemSize_ForceSize;
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_fword : InstrInfo {
		readonly CodeSize codeSize;
		readonly bool forceNoMemSize;
		readonly FormatterString mnemonic;
		readonly FormatterString mnemonic2;

		public SimpleInstrInfo_fword(CodeSize codeSize, bool forceNoMemSize, string mnemonic, string mnemonic2) {
			this.codeSize = codeSize;
			this.forceNoMemSize = forceNoMemSize;
			this.mnemonic = new FormatterString(mnemonic);
			this.mnemonic2 = new FormatterString(mnemonic2);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			FormatterString mnemonic;
			if (instruction.CodeSize == codeSize || instruction.CodeSize == CodeSize.Unknown)
				mnemonic = this.mnemonic;
			else
				mnemonic = mnemonic2;
			if (!forceNoMemSize)
				flags |= InstrOpInfoFlags.ShowNoMemSize_ForceSize;
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_jcc : InstrInfo {
		readonly int ccIndex;
		readonly FormatterString[] mnemonics;

		public SimpleInstrInfo_jcc(int ccIndex, string[] mnemonics) {
			this.ccIndex = ccIndex;
			this.mnemonics = FormatterString.Create(mnemonics);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			var prefixSeg = instruction.SegmentPrefix;
			if (prefixSeg == Register.CS)
				flags |= InstrOpInfoFlags.JccNotTaken;
			else if (prefixSeg == Register.DS)
				flags |= InstrOpInfoFlags.JccTaken;
			if (instruction.HasRepnePrefix)
				flags |= InstrOpInfoFlags.BndPrefix;
			var mnemonic = MnemonicCC.GetMnemonicCC(options, ccIndex, mnemonics);
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_bnd : InstrInfo {
		readonly FormatterString mnemonic;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_bnd(string mnemonic) : this(mnemonic, InstrOpInfoFlags.None) { }

		public SimpleInstrInfo_bnd(string mnemonic, InstrOpInfoFlags flags) {
			this.mnemonic = new FormatterString(mnemonic);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = this.flags;
			if (instruction.HasRepnePrefix)
				flags |= InstrOpInfoFlags.BndPrefix;
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_pops : InstrInfo {
		readonly FormatterString mnemonic;
		readonly FormatterString[] pseudo_ops;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_pops(string mnemonic, FormatterString[] pseudo_ops) : this(mnemonic, pseudo_ops, InstrOpInfoFlags.None) { }

		public SimpleInstrInfo_pops(string mnemonic, FormatterString[] pseudo_ops, InstrOpInfoFlags flags) {
			this.mnemonic = new FormatterString(mnemonic);
			this.pseudo_ops = pseudo_ops;
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, flags);
			int imm = instruction.Immediate8;
			if (options.UsePseudoOps && (uint)imm < (uint)pseudo_ops.Length) {
				info.Mnemonic = pseudo_ops[imm];
				RemoveLastOp(ref info);
			}
		}

		internal static void RemoveLastOp(ref InstrOpInfo info) {
			switch (info.OpCount) {
			case 4:
				info.Op3Index = OpAccess_INVALID;
				break;
			case 3:
				info.Op2Index = OpAccess_INVALID;
				break;
			default:
				throw new InvalidOperationException();
			}
			info.OpCount--;
		}
	}

	sealed class SimpleInstrInfo_pclmulqdq : InstrInfo {
		readonly FormatterString mnemonic;
		readonly FormatterString[] pseudo_ops;

		public SimpleInstrInfo_pclmulqdq(string mnemonic, FormatterString[] pseudo_ops) {
			this.mnemonic = new FormatterString(mnemonic);
			this.pseudo_ops = pseudo_ops;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
			if (options.UsePseudoOps) {
				int index;
				int imm = instruction.Immediate8;
				if (imm == 0)
					index = 0;
				else if (imm == 1)
					index = 1;
				else if (imm == 0x10)
					index = 2;
				else if (imm == 0x11)
					index = 3;
				else
					index = -1;
				if (index >= 0) {
					info.Mnemonic = pseudo_ops[index];
					SimpleInstrInfo_pops.RemoveLastOp(ref info);
				}
			}
		}
	}

	sealed class SimpleInstrInfo_imul : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_imul(string mnemonic) {
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
			Debug.Assert(info.OpCount == 3);
			if (options.UsePseudoOps && info.Op0Kind == InstrOpKind.Register && info.Op1Kind == InstrOpKind.Register && info.Op0Register == info.Op1Register) {
				info.OpCount--;
				info.Op0Index = OpAccess_ReadWrite;
				info.Op1Kind = info.Op2Kind;
				info.Op1Index = 2;
				info.Op2Index = OpAccess_INVALID;
			}
		}
	}

	sealed class SimpleInstrInfo_Reg16 : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_Reg16(string mnemonic) =>
			this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			const InstrOpInfoFlags flags = InstrOpInfoFlags.None;
			info = new InstrOpInfo(mnemonic, instruction, flags);
			if (Register.EAX <= (Register)info.Op0Register && (Register)info.Op0Register <= Register.R15D) {
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)((Register)info.Op0Register - Register.EAX + Register.AX);
			}
			if (Register.EAX <= (Register)info.Op1Register && (Register)info.Op1Register <= Register.R15D) {
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op1Register = (byte)((Register)info.Op1Register - Register.EAX + Register.AX);
			}
		}
	}

	sealed class SimpleInstrInfo_reg : InstrInfo {
		readonly FormatterString mnemonic;
		readonly Register register;

		public SimpleInstrInfo_reg(string mnemonic, Register register) {
			this.mnemonic = new FormatterString(mnemonic);
			this.register = register;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = default;
			info.Mnemonic = mnemonic;
			info.OpCount = 1;
			info.Op0Kind = InstrOpKind.Register;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op0Register = (byte)register;
			info.Op0Index = OpAccess_Read;
		}
	}

	sealed class SimpleInstrInfo_invlpga : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_invlpga(int bitness, string mnemonic) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = default;
			info.Mnemonic = mnemonic;
			info.OpCount = 2;
			info.Op0Kind = InstrOpKind.Register;
			info.Op1Kind = InstrOpKind.Register;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op1Register = (byte)Register.ECX;
			info.Op0Index = OpAccess_Read;
			info.Op1Index = OpAccess_Read;

			switch (bitness) {
			case 16:
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)Register.AX;
				break;

			case 32:
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)Register.EAX;
				break;

			case 64:
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)Register.RAX;
				break;

			default:
				throw new InvalidOperationException();
			}
		}
	}

	sealed class SimpleInstrInfo_DeclareData : InstrInfo {
		readonly FormatterString mnemonic;
		readonly InstrOpKind opKind;

		public SimpleInstrInfo_DeclareData(Code code, string mnemonic) {
			this.mnemonic = new FormatterString(mnemonic);
			opKind = code switch {
				Code.DeclareByte => InstrOpKind.DeclareByte,
				Code.DeclareWord => InstrOpKind.DeclareWord,
				Code.DeclareDword => InstrOpKind.DeclareDword,
				Code.DeclareQword => InstrOpKind.DeclareQword,
				_ => throw new InvalidOperationException(),
			};
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.MnemonicIsDirective);
			info.OpCount = (byte)instruction.DeclareDataCount;
			info.Op0Kind = opKind;
			info.Op1Kind = opKind;
			info.Op2Kind = opKind;
			info.Op3Kind = opKind;
			info.Op4Kind = opKind;
			info.Op0Index = OpAccess_Read;
			info.Op1Index = OpAccess_Read;
			info.Op2Index = OpAccess_Read;
			info.Op3Index = OpAccess_Read;
			info.Op4Index = OpAccess_Read;
		}
	}
}
#endif
